package server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import pojo.ChessOnlineRoom;
import pojo.League;
import pojo.UserAccount;
import com.thuan.hibernate.LeagueTimer;
import com.thuan.hibernate.Repository;
import transfer.*;

public class Worker implements Runnable {
	int id;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	Server server;
	Socket clientSocket;
	public BufferedReader br = null;
	public PrintWriter pw = null;

	public ObjectOutputStream oos = null;
	public ObjectInputStream ois = null;

	public Worker(Socket client, Server server, int id) throws IOException {
		this.id = id;
		this.server = server;
		this.clientSocket = client;
		br = new BufferedReader(new InputStreamReader(
				clientSocket.getInputStream()));
		pw = new PrintWriter(new OutputStreamWriter(
				clientSocket.getOutputStream()));

		oos = new ObjectOutputStream(clientSocket.getOutputStream());
		ois = new ObjectInputStream(clientSocket.getInputStream());
		oos.flush();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			clientLoop();
		} catch (Exception e) {
			e.printStackTrace();
		} 
		finally { 
			try {
				System.out.println("Client " + id + " has left");
				server.removeWorker(id);
				clientSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				}
		 
	}


	private void clientLoop() throws IOException {
		while (true) {
			Data data;
			if ((data = (Data) receiveObject()) != null) {
				if(data.getHeader().equals("Login")){
					LoginData lData = (LoginData)data;
					UserAccount acc = Repository.findUser(lData.getUsername());
					if( acc != null && acc.getPassword().equals(lData.getPassword())){
						Repository.updateSigninTime(acc.getUsername());
						sendObject(new UserAccountData("Account", acc));
						sendObject(new RoomData("List Rooms",server.getRooms()));
					}
				}
				if(data.getHeader().equals("Register")){
					RegisterData rData = (RegisterData)data;
					int result = Repository.addUser(rData.getUsername(), rData.getPassword(), rData.getName(), 1);
					if(result != -1){
						sendObject(new UserAccountData("Account", Repository.findUser(rData.getUsername())));
					}
					else{
						sendObject(new UserAccountData("Account", null));
					}		
				}
				if(data.getHeader().equals("Create Room")  ){
					
					CreateRoomData rData = (CreateRoomData)data;
					ChessOnlineRoom room = new ChessOnlineRoom(server.getRoomIndex(),rData.getName() , rData.getOwner(),
							null, rData.getScore(), false, -1, id,-1);
					server.addRoom(room);
					server.sendObject(new RoomData("List Rooms",server.getRooms()));
					sendObject(new IntegerData("Your Room",room.getRoomId()));		
					
				}
				if(data.getHeader().equals("Close Room")){
					IntegerData iData = (IntegerData)data;
					int idRoom = iData.getNum();
					ChessOnlineRoom room = server.getRooms().get(idRoom);
					int idWorkerB = room.getmWorkerIdB();			
					server.removeRoom(idRoom);
					System.out.println("Current List Rooms: " + server.getRooms().size());
					
					if(room.isLeague()){
						sendObject(new IntegerData("Close Your Room", 1));
						if(idWorkerB != -1){ // have player B
							server.sendObjectToId(new IntegerData("Close Your Room", 1),idWorkerB);
						}
						server.sendObject(new LeagueRoomData("List League Rooms", room.getLeagueId(),server.getRooms()));
					}
					else{
						sendObject(new IntegerData("Close Your Room", 0));
						if(idWorkerB != -1){ // have player B
							server.sendObjectToId(new IntegerData("Close Your Room", 0),idWorkerB);
						}
						server.sendObject(new RoomData("List Rooms",server.getRooms()));
					}				
				}
				if(data.getHeader().equals("Join Room")){
					JoinRoomData jData = (JoinRoomData)data;
					ChessOnlineRoom room = server.getRooms().get(jData.getId());
					room.setPlayerB(jData.getAcc());
					room.setmWorkerIdB(id);
					server.sendObject(new RoomData("List Rooms",server.getRooms()));
					sendObject(new IntegerData("Can Join",room.getRoomId()));
					server.sendObjectToId(new IntegerData("Update Room",room.getRoomId()) ,room.getmWorkerIdA());
				}
				if(data.getHeader().equals("Quit Room")){
					IntegerData iData = (IntegerData)data;
					ChessOnlineRoom room = server.getRooms().get(iData.getNum());
					room.setPlayerB(null);
					room.setmWorkerIdB(-1);
					if(room.isLeague()){
						sendObject(new IntegerData("Can Quit", 1));
						server.sendObject(new LeagueRoomData("List League Rooms", room.getLeagueId(), server.getRooms()));
					}
					else{
						sendObject(new IntegerData("Can Quit", 0));
						server.sendObject(new RoomData("List Rooms", server.getRooms()));
					}
					server.sendObjectToId(new IntegerData("Update Room",room.getRoomId()) ,room.getmWorkerIdA());
				}
				if(data.getHeader().equals("Start Game")){
					IntegerData iData = (IntegerData)data;
					ChessOnlineRoom room = server.getRooms().get(iData.getNum());
					server.sendObjectToId(new StringData("Your team is A",null), room.getmWorkerIdA());
					server.sendObjectToId(new StringData("Your team is B",null), room.getmWorkerIdB());
					server.sendObjectToAllInRoom(new IntegerData("Current turn",0), room.getRoomId());
					server.setCurrentTurn(0); //Team A play first
					if(room.isLeague())
						server.sendObject(new LeagueRoomData("List League Rooms", room.getLeagueId(), server.getRooms()));
				}
				if (data.getHeader().equals("Finish turn")) {
					ChessData cData = (ChessData)data;
					server.sendObjectToAnotherInRoom(new ChessData("Refresh board", cData.getArrayChess(), -1),
							cData.getRoomID(), id);			
					int turn = server.getCurrentTurn();
					if (turn == 1) {
						turn = 0;
					} else {
						turn = 1;
					}
					server.setCurrentTurn(turn);
				}
				if (data.getHeader().equals("Get new turn")) {
					IntegerData iData = (IntegerData) data;
					int turn = server.getCurrentTurn();	
					server.sendObjectToAllInRoom(new IntegerData("Current turn",turn),iData.getNum());
				}
				if(data.getHeader().equals("Chat content")){
					ChatData cData = (ChatData)data;
					server.sendObjectToAnotherInRoom(new StringData("Chat content",cData.getContent()),
							cData.getRoomID(),id);
				}
				if(data.getHeader().equals("End Game")){
					ChatData cData = (ChatData)data;
					ChessOnlineRoom room = server.getRooms().get(cData.getRoomID());
					String usernameWinner = room.getPlayerA().getUsername();
					
					if(cData.getContent().equals("Team A Win")){				
						UserAccount playerA = room.getPlayerA();
						playerA.setScore(playerA.getScore()+room.getScore());
						playerA.setWin(playerA.getWin()+1);
						Repository.updateScore(playerA.getUsername(), playerA.getScore(),
								playerA.getWin(), playerA.getLose());
						UserAccount playerB = room.getPlayerB();
						playerB.setLose(playerB.getLose()+1);
						Repository.updateScore(playerB.getUsername(), playerB.getScore(),
								playerB.getWin(), playerB.getLose());
						
						if(room.isLeague()){
							Repository.updateLeagueScore(room.getLeagueId(), playerA.getUsername(), room.getScore());
							Repository.updateLeagueScore(room.getLeagueId(), playerB.getUsername(), 0);
						}
						
						server.sendObjectToId(new UserAccountData("You win",playerA), room.getmWorkerIdA());
						server.sendObjectToId(new UserAccountData("You lose",playerB), room.getmWorkerIdB());
					}
					else{
						usernameWinner = room.getPlayerB().getUsername();
						UserAccount playerA = room.getPlayerA();
						playerA.setLose(playerA.getLose()+1);
						Repository.updateScore(playerA.getUsername(), playerA.getScore(),playerA.getWin(), playerA.getLose());
						UserAccount playerB = room.getPlayerB();
						playerB.setScore(playerB.getScore()+room.getScore());
						playerB.setWin(playerB.getWin()+1);
						Repository.updateScore(playerB.getUsername(), playerB.getScore(),
								playerB.getWin(), playerB.getLose());
						
						if(room.isLeague()){
							Repository.updateLeagueScore(room.getLeagueId(), playerA.getUsername(), 0);
							Repository.updateLeagueScore(room.getLeagueId(), playerB.getUsername(), room.getScore());
						}
						
						server.sendObjectToId(new UserAccountData("You win",playerB), room.getmWorkerIdB());
						server.sendObjectToId(new UserAccountData("You lose",playerA), room.getmWorkerIdA());
					}
					
					server.removeRoom(cData.getRoomID());
					
					if(room.isLeague()){
						League league = Repository.findLeague(room.getLeagueId());
						if(league.getEndDate().compareTo(new Date()) < 0 || league.getStartDate().compareTo(new Date()) > 0){
							sendObject(new UserAccountData("Account", room.getPlayerA()));
							sendObject(new UserAccountData("Account", room.getPlayerB()));
							ArrayList<League> list = new ArrayList<League>(Repository.listLeagues());
							server.sendObject(new LeagueData("List Leagues", room.getPlayerA(), list));
							server.sendObject(new LeagueData("List Leagues", room.getPlayerB(), list));
						}
						else{
							server.sendObjectToId(new StringData("Show League Lobby View", null), room.getmWorkerIdA());
							server.sendObjectToId(new StringData("Show League Lobby View", null), room.getmWorkerIdB());
							server.sendObject(new LeagueRoomData("List League Rooms", room.getLeagueId(), server.getRooms()));
						}
						Repository.addCompetition(room.getLeagueId(), room.getPlayerA().getUsername(), room.getPlayerB().getUsername(), usernameWinner);
					}
					else{
						server.sendObject(new RoomData("List Rooms",server.getRooms()));
					}				
				}
				if(data.getHeader().equals("Quit Game")){
					ChatData cData = (ChatData)data;
					ChessOnlineRoom room = server.getRooms().get(cData.getRoomID());
					String usernameWinner = room.getPlayerA().getUsername();
					
					if(cData.getContent().equals("Team A")){
						usernameWinner = room.getPlayerB().getUsername();
						UserAccount playerA = room.getPlayerA();
						playerA.setLose(playerA.getLose()+1);
						Repository.updateScore(playerA.getUsername(), playerA.getScore(),
								playerA.getWin(), playerA.getLose());
						UserAccount playerB = room.getPlayerB();
						playerB.setScore(playerB.getScore()+room.getScore());
						playerB.setWin(playerB.getWin()+1);
						Repository.updateScore(playerB.getUsername(), playerB.getScore(),
								playerB.getWin(), playerB.getLose());
						
						if(room.isLeague()){
							Repository.updateLeagueScore(room.getLeagueId(), playerA.getUsername(), 0);
							Repository.updateLeagueScore(room.getLeagueId(), playerB.getUsername(), room.getScore());
						}
							
						server.sendObjectToId(new UserAccountData("You win",playerB), room.getmWorkerIdB());
						server.sendObjectToId(new UserAccountData("You lose",playerA), room.getmWorkerIdA());
					}
					else{
						UserAccount playerA = room.getPlayerA();
						playerA.setScore(playerA.getScore()+room.getScore());
						playerA.setWin(playerA.getWin()+1);
						Repository.updateScore(playerA.getUsername(), playerA.getScore(),
								playerA.getWin(), playerA.getLose());
						UserAccount playerB = room.getPlayerB();
						playerB.setLose(playerB.getLose()+1);
						Repository.updateScore(playerB.getUsername(), playerB.getScore(),
								playerB.getWin(), playerB.getLose());

						if(room.isLeague()){
							Repository.updateLeagueScore(room.getLeagueId(), playerA.getUsername(), room.getScore());
							Repository.updateLeagueScore(room.getLeagueId(), playerB.getUsername(), 0);
						}
						
						server.sendObjectToId(new UserAccountData("You win",playerA), room.getmWorkerIdA());
						server.sendObjectToId(new UserAccountData("You lose",playerB), room.getmWorkerIdB());
					}
					// send list room
					server.removeRoom(cData.getRoomID());
					
					if(room.isLeague()){
						League league = Repository.findLeague(room.getLeagueId());
						if(league.getEndDate().compareTo(new Date()) < 0 || league.getStartDate().compareTo(new Date()) > 0){
							sendObject(new UserAccountData("Account", room.getPlayerA()));
							sendObject(new UserAccountData("Account", room.getPlayerB()));
							ArrayList<League> list = new ArrayList<League>(Repository.listLeagues());
							server.sendObject(new LeagueData("List Leagues", room.getPlayerA(), list));
							server.sendObject(new LeagueData("List Leagues", room.getPlayerB(), list));
						}
						else{
							server.sendObjectToId(new StringData("Show League Lobby View", null), room.getmWorkerIdA());
							server.sendObjectToId(new StringData("Show League Lobby View", null), room.getmWorkerIdB());
							server.sendObject(new LeagueRoomData("List League Rooms", room.getLeagueId(), server.getRooms()));
						}
						Repository.addCompetition(room.getLeagueId(), room.getPlayerA().getUsername(), room.getPlayerB().getUsername(), usernameWinner);
					}
					else server.sendObject(new RoomData("List Rooms",server.getRooms()));	
				}
				
				
				if(data.getHeader().equals("Disconnect")){
					break;
				}

				
				if(data.getHeader().equals("List League")){
					LeagueData lData = (LeagueData)data;
					sendObject(new UserAccountData("Account", lData.getAcc()));
					ArrayList<League> list = new ArrayList<League>(Repository.listLeagues());
					LeagueData dt = new LeagueData("List Leagues", lData.getAcc(), list);
					server.sendObject(dt);
				}

				if(data.getHeader().equals("Join League")){
					JoinLeagueData jlData = (JoinLeagueData)data;
					League league = Repository.findLeague(jlData.getId());
					if(league.getEndDate().compareTo(new Date()) < 0 || league.getStartDate().compareTo(new Date()) > 0){
						sendObject(new UserAccountData("Account", jlData.getAcc()));
						ArrayList<League> list = new ArrayList<League>(Repository.listLeagues());
						LeagueData dt = new LeagueData("List Leagues", jlData.getAcc(), list);
						server.sendObject(dt);
					}
					else{
						if(Repository.countMemberOfLeague(league) == 0)
							new LeagueTimer(server, league, 0, 20*60); //seconds
						server.sendObjectToId(new StringData("Show League Lobby View", null), id);
						Repository.addLeagueMember(jlData.getId(), jlData.getAcc().getUsername(), 0);
						server.sendObject(new LeagueRoomData("List League Rooms", jlData.getId(), server.getRooms()));
					}
				}
				
				if(data.getHeader().equals("Create League Room")){
					CreateLeagueRoomData lrData = (CreateLeagueRoomData)data;
					League league = Repository.findLeague(lrData.getLeagueId());
					if(league.getEndDate().compareTo(new Date()) < 0 || league.getStartDate().compareTo(new Date()) > 0){
						sendObject(new UserAccountData("Account", lrData.getOwner()));
						ArrayList<League> list = new ArrayList<League>(Repository.listLeagues());
						LeagueData dt = new LeagueData("List Leagues", lrData.getOwner(), list);
						server.sendObject(dt);
					}
					else
					{
						ChessOnlineRoom leagueroom = new ChessOnlineRoom(server.getRoomIndex(),lrData.getName() , lrData.getOwner(),
								null, lrData.getScore(), true, lrData.getLeagueId(), id,-1);
						server.addRoom(leagueroom);
						server.sendObject(new LeagueRoomData("List League Rooms", lrData.getLeagueId(), server.getRooms()));
						sendObject(new IntegerData("Your Room", leagueroom.getRoomId()));		
					}
				}
				
			}
		}
	}

/*	public String getStringData() {
		String line = null;
		try {
			line = br.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}

		return line;
	}

	public void sendStringData(String s) {
		pw.println(s);
		pw.flush();

	}*/
	public void sendObject(Object obj) {
		try {
			oos.reset();
			oos.writeObject(obj);
			oos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Object receiveObject() {
		Object obj = null;
		try {
			obj = ois.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return obj;
	}
}
